#!/usr/bin
# -*- coding: utf-8 -*-

import parser as pr
from copy import deepcopy as dcp
import sys
import gc

# ==============================================================================
class Problem():
    def __init__(self, inputFileName, inputFileType=None):
        self.inputFileName = inputFileName
        self.inputFileType = inputFileType

        #TODO: create a GlobalDomain object
        #      containing and managing all that follows
        self.args = {}

        self.args["nNodes"] = 0
        self.args["ndsU"] = []
        self.args["ndsC"] = []
        self.args["ndsC2U"] = []
        self.args["ndsU2C"] = {}

        self.args["nElements"] = 0
        self.args["elsU"] = []
        self.args["elsC"] = []
        self.args["elsC2U"] = []
        self.args["elsU2C"] = {}

        self.args["nSections"] = 0
        self.args["secsU"] = []
        self.args["secsC"] = []
        self.args["secsC2U"] = []
        self.args["secsU2C"] = {}

        self.args["nMaterials"] = 0
        self.args["matsU"] = []
        self.args["matsC"] = []
        self.args["matsC2U"] = []
        self.args["matsU2C"] = {}

        self.args["nDomains"] = 0
        self.args["domsU"] = []
        self.args["domsC"] = []
        self.args["domsC2U"] = []
        self.args["domsU2C"] = {}

        self.args["nTopologicDomains"] = 0
        self.args["topDomsU"] = []
        self.args["topDomsC"] = []
        self.args["topDomsC2U"] = []
        self.args["topDomsU2C"] = {}

        self.args["nBoundaryConditions"] = 0
        self.args["bcsU"] = []
        self.args["bcsC"] = []
        self.args["bcsC2U"] = []
        self.args["bcsU2C"] = {}

        self.args["nLoadConditions"] = 0
        self.args["lcsU"] = []
        self.args["lcsC"] = []
        self.args["lcsC2U"] = []
        self.args["lcsU2C"] = {}

        self.args["ndsCIdsFromDomCId"] = []
        self.args["elsCIdsFromNdCId"] = []
        self.args["domsCIdsFromElCId"] = []
        self.args["domsCIdsFromNdCId"] = []

    def read(self):
        """ This function reads the input file and builds the native types
        of data (native: the closest from the file description)
        """
        pr.readFile(self.inputFileName, self.inputFileType, self.args)

    def buildCodeDataStructure(self):
        """ This function builds the CODE data structure,
        i.e. the one and only one used from now
        """
        # Nodes
        self.args["ndsC"] = dcp(self.args["ndsU"])
        self.args.__delitem__("ndsU")

        for ndCId, nd in enumerate(self.args["ndsC"]):
            nd.cId = ndCId
            self.args["ndsC2U"].append(nd.uId)
            self.args["ndsU2C"][nd.uId] = ndCId
            del(nd.uId)

        # Materials
        self.args["matsC"] = dcp(self.args["matsU"])
        self.args.__delitem__("matsU")

        for matCId, mat in enumerate(self.args["matsC"]):
            mat.cId = matCId
            self.args["matsC2U"].append(mat.uId)
            self.args["matsU2C"][mat.uId] = matCId
            del(mat.uId)

        # Sections
        self.args["secsC"] = dcp(self.args["secsU"])
        self.args.__delitem__("secsU")

        for secCId, sec in enumerate(self.args["secsC"]):
            sec.cId = secCId
            self.args["secsC2U"].append(sec.uId)
            self.args["secsU2C"][sec.uId] = secCId
            del(sec.uId)

            # Association of the material to the section
            matUId = sec.matUId
            matCId = self.args["matsU2C"][matUId]
            sec.matCId = matCId
            sec.setMaterial(self.args["matsC"][matCId])
            del(sec.matUId)

        # Elements
        self.args["elsC"] = dcp(self.args["elsU"])
        self.args.__delitem__("elsU")

        for elCId, el in enumerate(self.args["elsC"]):
            el.cId = elCId
            self.args["elsC2U"].append(el.uId)
            self.args["elsU2C"][el.uId] = elCId
            del(el.uId)

            # Association of the nodes to the element
            ndsUIds = dcp(el.ndsUIds)
            el.nds = []
            el.ndsCIds = []
            for ndUId in ndsUIds:
                ndCId = self.args["ndsU2C"][ndUId]
                nd = self.args["ndsC"][ndCId]
                el.nds.append(nd)
                el.ndsCIds.append(ndCId)
            el.nds = tuple(el.nds)
            el.ndsCIds = tuple(el.ndsCIds)
            del(el.ndsUIds)

            # Association of the section to the element
            secUId = el.secUId
            secCId = self.args["secsU2C"][secUId]
            el.secCId = secCId
            el.setSection(self.args["secsC"][secCId])
            del(el.secUId)

        # TopologicDomains
        self.args["topDomsC"] = dcp(self.args["topDomsU"])
        self.args.__delitem__("topDomsU")

        for topDomCId, topDom in enumerate(self.args["topDomsC"]):
            topDom.cId = topDomCId
            self.args["topDomsC2U"].append(topDom.uId)
            self.args["topDomsU2C"][topDom.uId] = topDomCId
            del(topDom.uId)

            # Association of the nodes to the topologicDomain
            ndsUIds = dcp(topDom.ndsUIds)
            topDom.nds = []
            topDom.ndsCIds = []
            for ndUId in ndsUIds:
                ndCId = self.args["ndsU2C"][ndUId]
                nd = self.args["ndsC"][ndCId]
                topDom.nds.append(nd)
                topDom.ndsCIds.append(ndCId)
            topDom.nds = tuple(topDom.nds)
            topDom.ndsCIds = tuple(topDom.ndsCIds)
            del(topDom.ndsUIds)

        # Domains
        self.args["domsC"] = dcp(self.args["domsU"])
        self.args.__delitem__("domsU")

        for domCId, dom in enumerate(self.args["domsC"]):
            dom.cId = domCId
            self.args["domsC2U"].append(dom.uId)
            self.args["domsU2C"][dom.uId] = domCId

            # Association of the elements to the domain
            elsUIds = dcp(dom.elsUIds)
            dom.els = []
            dom.elsCIds = []
            for elUId in elsUIds:
                elCId = self.args["elsU2C"][elUId]
                el = self.args["elsC"][elCId]
                dom.els.append(el)
                dom.elsCIds.append(elCId)
            dom.els = tuple(dom.els)
            dom.elsCIds = tuple(dom.elsCIds)
            del(dom.elsUIds)

            # Association of the topologicDomain to the domain
            try:
                topDomCId = self.args["topDomsU2C"][dom.uId]
                dom.cornerNdsCIds = dcp(self.args["topDomsC"][topDomCId].ndsCIds)
            except:
                raise ValueError, "A Domain must have an associated " + \
                                  "TopologicDomain"
            del(dom.uId)
        self.args.__delitem__("topDomsC")

        # BoundaryConditions
        self.args["bcsC"] = dcp(self.args["bcsU"])
        self.args.__delitem__("bcsU")

        for bcCId, bc in enumerate(self.args["bcsC"]):
            bc.cId = bcCId
            self.args["bcsC2U"].append(bc.uId)
            self.args["bcsU2C"][bc.uId] = bcCId
            bc.ndCId = self.args["ndsU2C"][bc.ndUId]
            del(bc.uId, bc.ndUId)

        # LoadConditions
        self.args["lcsC"] = dcp(self.args["lcsU"])
        self.args.__delitem__("lcsU")

        for lcCId, lc in enumerate(self.args["lcsC"]):
            lc.cId = bcCId
            self.args["lcsC2U"].append(lc.uId)
            self.args["lcsU2C"][lc.uId] = lcCId
            lc.ndCId = self.args["ndsU2C"][lc.ndUId]
            del(lc.uId, lc.ndUId)

        # Extra connectivity

        #TODO: look for doing several things in less loops to gain efficiency

        # ndsCIdsFromDomCId
        for domCId, dom in enumerate(self.args["domsC"]):
            self.args["ndsCIdsFromDomCId"].append({})
            for els in dom.els:
                ndsCIds = els.ndsCIds
                for ndCId in ndsCIds:
                    try:
                        a = self.args["ndsCIdsFromDomCId"][-1][ndCId]
                    except:
                        self.args["ndsCIdsFromDomCId"][-1][ndCId] = None
            ndsCIds = self.args["ndsCIdsFromDomCId"][-1].keys()
            ndsCIds.sort()
            self.args["ndsCIdsFromDomCId"][-1] = tuple(ndsCIds)

        # elsCIdsFromNdCId
        tempElsCIdsFromNdCId = {}
        for elCId, el in enumerate(self.args["elsC"]):
            ndsCIds = el.ndsCIds
            for ndCId in ndsCIds:
                try:
                    a = tempElsCIdsFromNdCId[ndCId]
                    a.append(elCId) # as we loop over elCId we are sure that
                                    # elCId is not in a
                except:
                    tempElsCIdsFromNdCId[ndCId] = [elCId]

        for ndCId, elsCIds in tempElsCIdsFromNdCId.items():
            elsCIds.sort()
            self.args["elsCIdsFromNdCId"].append(tuple(elsCIds))

        del(tempElsCIdsFromNdCId)

        # domsCIdsFromElCId -- HERE WE ASSUME AN ELEMENT CAN BE PART OF
        #                      SEVERAL DOMAINS
        tempDomsCIdsFromElCId = {}
        for domCId, dom in enumerate(self.args["domsC"]):
            for elCId in dom.elsCIds:
                try:
                    a = tempDomsCIdsFromElCId[elCId]
                    a.append(domCId) # as we loop over domCId we are sure that
                                     # domCId is not yet in a
                except:
                    tempDomsCIdsFromElCId[elCId] = [domCId]

        for elCId, domsCIds in tempDomsCIdsFromElCId.items():
            if len(domsCIds) > 1:
                domsCIds.sort()
            self.args["domsCIdsFromElCId"].append(tuple(domsCIds))

        del(tempDomsCIdsFromElCId)

        # domsCIdsFromNdCId -- HERE WE ASSUME A NODE CAN BE PART OF
        #                      SEVERAL DOMAINS
        tempDomsCIdsFromNdCId = {}
        for domCId, dom in enumerate(self.args["domsC"]):
            for ndCId in self.args["ndsCIdsFromDomCId"][domCId]:
                try:
                    a = tempDomsCIdsFromNdCId[ndCId]
                    a.append(domCId) # as we loop over domCId we are sure that
                                     # domCId is not yet in a
                except:
                    tempDomsCIdsFromNdCId[ndCId] = [domCId]

        for ndCId, domsCIds in tempDomsCIdsFromNdCId.items():
            if len(domsCIds) > 1:
                domsCIds.sort()
            self.args["domsCIdsFromNdCId"].append(tuple(domsCIds))

        del(tempDomsCIdsFromNdCId)

    def buildEdges(self):
        for domCId, dom in enumerate(self.args["domsC"]):
            domNdsCIds = self.args["ndsCIdsFromDomCId"][domCId]
            nds = self.args["ndsC"]
            domNds = [nds[ndsCId] for ndsCId in domNdsCIds]
            dom.buildEdges(domNds, domNdsCIds)

# ==============================================================================
if __name__ == "__main__":

    inputFileName = "maillage6.dat"
    inputFileType = None

    myProblem = Problem(inputFileName, inputFileType)
    myProblem.read()

    print myProblem.args["ndsU"][0]
    print ""
    print myProblem.args["elsU"][0]
    print ""
    print myProblem.args["secsU"][0]
    print ""
    print myProblem.args["matsU"][0]
    print ""
    print myProblem.args["domsU"][0]
    print ""
    print myProblem.args["topDomsU"][0]
    print ""
    #print myProblem.args["bcsU"][0]
    print ""
    #print myProblem.args["lcsU"][0]
    print ""

    myProblem.buildCodeDataStructure()

    print "=" * 80
    print myProblem.args["ndsC"][0]
    print ""
    print myProblem.args["elsC"][1]
    print ""
    print myProblem.args["secsC"][0]
    print ""
    print myProblem.args["matsC"][0]
    print ""
    print myProblem.args["domsC"][0]
    print ""
    #print myProblem.args["bcsC"][0]
    print ""
    #print myProblem.args["lcsC"][0]
    print ""



    #print sys.getsizeof(myProblem)
    #gc.collect()

    print "ndsCIdsFromDomCId\n", myProblem.args["ndsCIdsFromDomCId"]
    print "elsCIdsFromNdCId\n", myProblem.args["elsCIdsFromNdCId"]
    print "domsCIdsFromElCId\n", myProblem.args["domsCIdsFromElCId"]
    print "domsCIdsFromNdCId\n", myProblem.args["domsCIdsFromNdCId"]

    myProblem.buildEdges()
    myProblem.buildInterDomainsBoundaries()

